package dipl.algorithm.math.fp.primitive.op;

import dipl.algorithm.math.fp.primitive.Matrix3x3f;
import dipl.algorithm.math.fp.primitive.Point2df;
import dipl.algorithm.math.fp.primitive.Point3df;

/**
 * Class provides methods to construct 2d affine transformation matrices
 */
public class AffineTransformations2d {

	//
	// PUBLIC METHODS
	//

	/**
	 * @return a reflection matrix about the x-axis
	 */
	public static Matrix3x3f ReflectionXAxis() {
		Matrix3x3f res = new Matrix3x3f();
		res.m[0][0] = 1.0; res.m[0][1] = 0.0;
		res.m[1][0] = 0.0; res.m[1][1] = -1.0;
		return res;
	}

	/**
	 * @return a reflection matrix about the y-axis
	 */
	public static Matrix3x3f ReflectionYAxis() {
		Matrix3x3f res = new Matrix3x3f();
		res.m[0][0] = -1.0; res.m[0][1] = 0.0;
		res.m[1][0] = 0.0; res.m[1][1] = 1.0;
		return res;
	}

	/**
	 * @param angleInRad in radians
	 * @return a rotation matrix for counterclockwise rotation around origin
	 */
	public static Matrix3x3f Rotation( double angleInRad ) {
		Matrix3x3f res = new Matrix3x3f();
		double sinRad = Math.sin( angleInRad );
		double cosRad = Math.cos( angleInRad );
		res.m[0][0] = cosRad; res.m[0][1] = -sinRad;
		res.m[1][0] = sinRad; res.m[1][1] = cosRad;
		return res;
	}

	/**
	 * @param scalex
	 * @param scaley
	 * @return a scaling matrix
	 */
	public static Matrix3x3f Scaling( double scalex, double scaley ) {
		Matrix3x3f res = new Matrix3x3f();
		res.m[0][0] = scalex;
		res.m[1][1] = scaley;
		return res;
	}

	/**
	 * @param shearx
	 * @param sheary
	 * @return a shearing matrix
	 */
	public static Matrix3x3f Shearing( double shearx, double sheary ) {
		Matrix3x3f res = new Matrix3x3f();
		res.m[0][1] = shearx;
		res.m[1][0] = sheary;
		return res;
	}

	/**
	 * Applies affine transformation given by matrix m to given 2d-point (x,y,1)
	 * resulting in a new point which is returned.
	 * @param m affine transformation matrix
	 * @param p point to transform
	 * @return new resulting point
	 */
	public static Point2df Transform( Matrix3x3f m, Point2df p ) {
		Point2df res = new Point2df();
		res.x = m.m[0][0]*p.x+m.m[1][0]*p.y+m.m[2][0];
		res.y = m.m[0][1]*p.x+m.m[1][1]*p.y+m.m[2][1];
		double h = m.m[0][2]*p.x+m.m[1][2]*p.y+m.m[2][2];
		if( h != 0.0 ) {
			res.x /= h;
			res.y /= h;
		}
		return res;
	}

	/**
	 * Applies affine transformation given by matrix m to given 2d-point (x,y,1).
	 * @param m
	 * @param p
	 */
	public static void TransformPoint( Matrix3x3f m, Point2df p /*out*/ ) {
		double tempx = m.m[0][0]*p.x+m.m[1][0]*p.y+m.m[2][0];
		double tempy = m.m[0][1]*p.x+m.m[1][1]*p.y+m.m[2][1];
		double h = m.m[0][2]*p.x+m.m[1][2]*p.y+m.m[2][2];
		p.x = tempx;
		p.y = tempy;
		if( h != 0.0 ) {
			p.x /= h;
			p.y /= h;
		}
	}

	/**
	 * Applies affine transformation given by matrix m to given projective 2d-point (x,y,w).
	 * @param m
	 * @param p
	 */
	public static void TransformPoint( Matrix3x3f m, /*out*/ Point3df p ) {
		double tempx = m.m[0][0]*p.x+m.m[1][0]*p.y+p.z*m.m[2][0];
		double tempy = m.m[0][1]*p.x+m.m[1][1]*p.y+p.z*m.m[2][1];
		double tempw = m.m[0][2]*p.x+m.m[1][2]*p.y+p.z*m.m[2][2];
		p.x = tempx;
		p.y = tempy;
		p.z = tempw;
	}

	/**
	 * @param dx
	 * @param dy
	 * @return a translation matrix
	 */
	public static Matrix3x3f Translation( double dx, double dy ) {
		Matrix3x3f res = new Matrix3x3f();
		res.m[2][0] = dx;
		res.m[2][1] = dy;
		return res;
	}
}
